<!DOCTYPE HTML>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="../resources/run-after-layout-and-paint.js"></script>

<!--
This is a test of the new (June 2016) bounds calculation code,
in which every node in the accessibility tree specifies its
bounding box and optional matrix transform relative to an
ancestor object in the tree.

This representation means that when a container element
scrolls, animates, or otherwise changes position on screen,
its descendants don't need to be updated because their
coordinates are all relative.

This test asserts that the bounding box we compute is the
same as what's computed by the DOM getBoundingClientRect
interface.
 -->

<div class="container">
    <input type="text" id="input">
    <input type="checkbox" id="checkbox">
    <input type="radio" id="radio">
    <select id="select">
      <option>Apple
      <option>Orange
    </select>
    <button id="button">Button</button>
    <h1 id="heading">Heading</h1>
    <p id="para">Para</p>
    <p>This para has one <span id="span" role="group">span</span></p>
    <ul id="ul">
      <li id="li1">List item 1</li>
      <li id="li2">List item 2</li>
    </ul>
    <div id="div">Div</div>
    <div id="border" style="border: 10px solid #ccc;">Border</div>
    <div id="padding" style="padding: 20px;">Padding</div>
    <div id="margin" style="margin: 30px;">Margin</div>
    <div id="border_padding_margin"
         style="border: 10px solid #eee; padding: 20px; margin: 30px;">
      Border Padding Margin
    </div>
    <svg id="svg" width="60" height="60">
      <circle role="button" id="svg_circle" r="25" cx="30" cy="30" stroke="blue" stroke-width="1"/>
    </svg>
    <p id="twolines">First line<br>Second line</p>
</div>

<script>
function assertDOMRectSameAsAXRect(id) {
    var element = document.getElementById(id);
    var bounds = element.getBoundingClientRect();
    var axObject = accessibilityController.accessibleElementById(id);

    // Due to rounding we won't get identical bounds as getBoundingClientRect(),
    // so allow the test to pass if we're within 1 pixel. The test examples
    // deliberately have borders, margins, and padding larger than 1 pixel to
    // ensure that this epsilon is not masking more serious errors.
    var epsilon = 1;

    assert_approx_equals(axObject.boundsX, bounds.left, epsilon, id + " left");
    assert_approx_equals(axObject.boundsY, bounds.top, epsilon, id + " top");
    assert_approx_equals(axObject.boundsWidth, bounds.width, epsilon, id + " width");
    assert_approx_equals(axObject.boundsHeight, bounds.height, epsilon, id + " height");
}

function assertStaticTextChildDOMRectSameAsAXRect(id, index) {
    var element = document.getElementById(id);
    var axObject = accessibilityController.accessibleElementById(id);
    var text = element.firstChild;
    for (var i = 0; i < index; i++)
        text = text.nextSibling;
    var axText = axObject.childAtIndex(index);
    assert_equals(text.nodeType, Node.TEXT_NODE, id + " child " + index + " nodeType");
    assert_equals(axText.role, "AXRole: AXStaticText", id + " AX child " + index + " role");
    var range = document.createRange();
    range.selectNode(text);
    var bounds = range.getBoundingClientRect();
    var epsilon = 1;
    assert_approx_equals(axText.boundsX, bounds.left, epsilon, id + " child " + index + " left");
    assert_approx_equals(axText.boundsY, bounds.top, epsilon, id + " child " + index + " top");
    assert_approx_equals(axText.boundsWidth, bounds.width, epsilon, id + " child " + index + " width");
    assert_approx_equals(axText.boundsHeight, bounds.height, epsilon, id + " child " + index + " height");
}

function assertOffsetContainerIs(id, containerId, opt_expectedResult) {
    if (opt_expectedResult === undefined)
        opt_expectedResult = true;
    var axObject = accessibilityController.accessibleElementById(id);
    var axContainer = accessibilityController.accessibleElementById(containerId);
    if (opt_expectedResult)
        assert_equals(axObject.offsetContainer(), axContainer, id + " offsetContainer");
    else
        assert_false(axObject.offsetContainer().isEqual(axContainer), id + " offsetContainer (should be false)");
}

function assertHasTransform(id, expected) {
    var axObject = accessibilityController.accessibleElementById(id);
    assert_equals(axObject.hasNonIdentityTransform(), expected, id + " hasNonIdentityTransform");
}

test(function(t) {
    assertDOMRectSameAsAXRect("input");
    assertDOMRectSameAsAXRect("checkbox");
    assertDOMRectSameAsAXRect("radio");
    assertDOMRectSameAsAXRect("button");
    assertDOMRectSameAsAXRect("heading");
    assertStaticTextChildDOMRectSameAsAXRect("heading", 0);
    assertDOMRectSameAsAXRect("para");
    assertStaticTextChildDOMRectSameAsAXRect("para", 0);
    assertDOMRectSameAsAXRect("span");
    assertStaticTextChildDOMRectSameAsAXRect("span", 0);
    assertDOMRectSameAsAXRect("ul");
    assertDOMRectSameAsAXRect("li1");
    assertDOMRectSameAsAXRect("li2");
    assertDOMRectSameAsAXRect("div");
    assertStaticTextChildDOMRectSameAsAXRect("div", 0);
    assertDOMRectSameAsAXRect("border");
    assertStaticTextChildDOMRectSameAsAXRect("border", 0);
    assertDOMRectSameAsAXRect("padding");
    assertStaticTextChildDOMRectSameAsAXRect("padding", 0);
    assertDOMRectSameAsAXRect("margin");
    assertStaticTextChildDOMRectSameAsAXRect("margin", 0);
    assertDOMRectSameAsAXRect("border_padding_margin", 0);
    assertStaticTextChildDOMRectSameAsAXRect("border_padding_margin", 0);
    assertDOMRectSameAsAXRect("svg");
    assertDOMRectSameAsAXRect("svg_circle");

    // Test both static text elements in <p>First line<br>Second line</p>.
    // We don't care about the bounding box of the <br> element in-between.
    assertStaticTextChildDOMRectSameAsAXRect("twolines", 0);
    assertStaticTextChildDOMRectSameAsAXRect("twolines", 2);
}, "Test computed AX rect for some common objects");
</script>

<div id="container2" class="container" role="group"
     style="position: absolute; left: 400px; top: 20px; border: 3px solid #eee; margin: 5px; padding: 7px;">
    <div id="div2">Absolute-positioned</div>
    <svg id="svg2" width="60" height="60">
        <circle role="button" id="svg_circle2" r="25" cx="30" cy="30" stroke="blue" stroke-width="1"/>
    </svg>

</div>

<script>
test(function(t) {
    assertHasTransform("container2", false);

    assertHasTransform("div2", false);
    assertDOMRectSameAsAXRect("div2");
    assertOffsetContainerIs("div2", "container2");
    assertDOMRectSameAsAXRect("svg2");
    assertDOMRectSameAsAXRect("svg_circle2");
}, "Test objects inside of absolute-positioned container");
</script>

<div id="container3" class="container" style="height: 100px; overflow: scroll;">
    <div id="div3" style="height: 500px;">Div</div>
    <input type="text" id="input3">
</div>

<script>
async_test(function(t) {
    document.getElementById("container3").scrollTop = 200;
    runAfterLayoutAndPaint(t.step_func_done(() => {
        assertHasTransform("container3", false);

        assertHasTransform("div3", false);
        assertDOMRectSameAsAXRect("div3");
        assertOffsetContainerIs("div3", "container3");
        assertHasTransform("input3", false);
        assertDOMRectSameAsAXRect("input3");
        assertOffsetContainerIs("input3", "container3");
    }));
}, "Test objects inside of scrolled container");
</script>

<div id="container4" class="container" style="width: 200px; transform: rotateZ(45deg);" role="group">
    <div id="div4">Rotated text</div>
</div>

<script>
test(function(t) {
    assertHasTransform("container4", true);

    assertHasTransform("div4", false);
    assertDOMRectSameAsAXRect("div4");
    assertOffsetContainerIs("div4", "container4");
}, "Test objects inside of rotated container");
</script>

<div class="container">
    <div style="margin: 20px; width: 200px; transform: scale(1.5);">
        <div id="scroll5" class="container" style="padding: 10px; height: 100px; overflow: scroll;">
            <div style="width: 200px; transform: rotateZ(175deg);">
                <div id="div5" style="margin: 30px; padding: 40px; border: 2px solid #eee;">
                    Text with lots of transformations
                </div>
            </div>
        </div>
    </div>
</div>

<script>
async_test(function(t) {
    document.getElementById("scroll5").scrollTop = 40;
    runAfterLayoutAndPaint(t.step_func_done(() => {
      assertDOMRectSameAsAXRect("div5");
    }));
}, "Test object inside of several nested containers with transforms and scrolling.");
</script>

<div id="container6a" class="container" role="group" style="position: relative; left: 10px;"
     aria-owns="div6d">
    <div id="div6a">A</div>
    <div id="div6b">B</div>
</div>

<div id="container6b" class="container" role="group" style="position: relative; left: 10px;"
     aria-owns="div6b">
    <div id="div6c">C</div>
    <div id="div6d">D</div>
</div>

<script>
test(function(t) {
    // This test uses aria-owns to rearrange children between ancestors,
    // to ensure that an object's offset container is not a node that's an
    // ancestor in the accessibility tree but not in the layout tree.
    //
    // Access both of these accessibility objects first to make sure that
    // the owned children are up to date before running assertions,
    // since aria-owns is processed lazily.
    var axContainer6a = accessibilityController.accessibleElementById("container6a");
    axContainer6a.childrenCount;
    var axContainer6b = accessibilityController.accessibleElementById("container6b");
    axContainer6b.childrenCount;

    assertOffsetContainerIs("div6a", "container6a", true);
    assertOffsetContainerIs("div6b", "container6a", false);
    assertOffsetContainerIs("div6b", "container6b", false);

    assertOffsetContainerIs("div6c", "container6b", true);
    assertOffsetContainerIs("div6d", "container6a", false);
    assertOffsetContainerIs("div6d", "container6b", false);
}, "Container must be an ancestor in both the AX tree and layout tree.");
</script>

<div id="container7" class="container" role="group"
     style="position: absolute; left: 600px; top: 20px; border: 3px solid #eee; margin: 5px; padding: 7px;">
    <span id="span7" role="group">
      Before
      <div style="width: 100px; height: 100px;">Block</div>
      After
    </span>
</div>

<script>
test(function(t) {
    assertHasTransform("container7", false);
    assertHasTransform("span7", false);
    assertDOMRectSameAsAXRect("span7");
}, "Test spans inside of absolute-positioned container");
</script>

<div class="container">
  Before
  <span id="span8" style="position:relative;">After</span>
</div>

<script>
test(function(t) {
    assertStaticTextChildDOMRectSameAsAXRect("span8", 0);
}, "Test node that's inline and relative-positioned");
</script>
